package com.grt192.controller.hauntedhouse;

import com.grt192.core.Mechanism;
import com.grt192.core.StepController;
import com.grt192.mechanism.hauntedhouse.HauntedHouseMechanism;
import com.grt192.event.component.SwitchListener;
import com.grt192.mechanism.hauntedhouse.HHLEDMechanism;
import com.grt192.sensor.GRTSwitch;
import java.util.Random;

/**
 * Actuates mechanisms automatically or manually. If MasterAutoSwitch is on
 * manual operation, moving switch will toggle actuator cylinder position.
 * If auto, moving switch will toggle auto operation. This also has
 * Network Control
 * @author data, ajc, nik.lele
 */
public class HauntedHouseController extends StepController implements SwitchListener {
    //sleep commands

    public static final boolean EXTEND = true;
    public static final boolean RETRACT = false;
    //delays arrays indicies
    private static final int MIN_OPEN = 0;
    private static final int MAX_OPEN = 1;
    private static final int MIN_CLOSED = 2;
    private static final int MAX_CLOSED = 3;
    //button states
    private boolean master;
    private boolean auto;
    private boolean enabled;
    //mechanism
    private HauntedHouseMechanism hhm;
    private String mechID;
    //switches
    private static GRTSwitch masterSwitch;
    private static GRTSwitch autoSwitch;
    private GRTSwitch mySwitch;
    private String switchID;
    //delays
    private int[] delays;
    private boolean randomDelay;
    private static Random r;

    public HauntedHouseController(int solPin, int switchPin, int[] delays) {
        initMechanism(solPin, switchPin);
        initSwitches(solPin, switchPin);
        initDelays(delays);
    }

    public void initDelays(int[] delays) {
        //set delays for auto function
        this.delays = delays;

        if(r == null) {
            r = new Random();
        }

    }

    public void initMechanism(int solPin, int switchPin) {
        //construct mechanism
        hhm = new HauntedHouseMechanism(solPin, switchPin);
        mechID = "HH" + solPin;
        System.out.println("Constructing " + mechID);
        addMechanism(mechID, hhm);
    }

    //TODO decide to implement or remove
    //not good for abstraction, but will surely work
    //call this from HHLEDController to add all switches to all of these
    //master and auto must be static
    public static void initMasterAutoSwitch() {
    }

    /**
     * Precondition: initMechanism() called
     * @param solPin
     * @param switchPin
     */
    public void initSwitches(int solPin, int switchPin) {
        //local switch
        switchID = "Switch" + switchPin;
        //listen for manual or auto commands
        mySwitch = (GRTSwitch) hhm.getSensor(switchID);
        mySwitch.addSwitchListener(this);

        //TODO fix instantiation of switches in this control
        if (masterSwitch == null) {
            masterSwitch = new GRTSwitch(9, 50, "MasterButton");
            autoSwitch = new GRTSwitch(10, 50, "AutoButton");

            masterSwitch.start();
            autoSwitch.start();
        }

        //TEST THE HASHTABLE--GETTING THE SWITCH FROM AN ALIEN MECH
//        Mechanism om = getMechanism(HHLEDController.MECHANISM_ID);
//        masterSwitch = (GRTSwitch) om.getSensor(HHLEDMechanism.MASTER_BUTTON_ID);
//        autoSwitch = (GRTSwitch) om.getSensor(HHLEDMechanism.AUTO_BUTTON_ID);

        //listen
        masterSwitch.addSwitchListener(this);
        autoSwitch.addSwitchListener(this);

        //set starting states of auto and master function
        master = HHLEDController.MASTER_DEFAULT_STATE;
        auto = HHLEDController.AUTO_DEFAULT_STATE;
    }

    public void act() {
        running = true;
        while (running) {
            doSleep(EXTEND);

            //autonomous loop
            if (enabled && master && auto) {
//                System.out.println("HauntedHouseController auto extend from " +mechID );
                //extend and wait
                hhm.extend();

                doSleep(RETRACT);
                if (master && auto) {
//                    System.out.println("HauntedHouseController auto retract from " + mechID );
                    hhm.retract();
                }
            }
        }
    }

    public void switchPressed(GRTSwitch source) {
        System.out.println("HauntedHouseController switch press from " + mechID);
        if (source == mySwitch) {
            enabled = true;
            if (!auto && master) {
                hhm.extend();
            }
        }
    }

    public void switchReleased(GRTSwitch source) {
        System.out.println("HauntedHouseController switch release from " + mechID);
        if (source == mySwitch) {
            enabled = false;
            if (!auto && master) {
                hhm.retract();
            }
        }

        if (source == masterSwitch) {
            master = !master;
            System.out.println("Master switch to " + master + "from " + mechID);
        }

        if (source == autoSwitch) {
            auto = !auto;
            System.out.println("Auto switch to " + auto + "from " + mechID);
        }

    }

    /**
     * Keep the Mechanism at a given state for a calculated amount of time
     * @param open flag for which type of delay is used-- open true for open
     */
    public void doSleep(boolean open) {
        try {
            Thread.sleep(getSleepTime(open));
        } catch (InterruptedException ex) {
            ex.printStackTrace();
        }
    }

    /**
     * Get the sleep time for a given state. This defaults to minimum if
     * !randomDelay
     * @param open flag for which type of delay used-- open true for open
     * @return time in ms for sleep
     */
    public long getSleepTime(boolean open) {
        int low = open ? delays[MIN_OPEN] : delays[MIN_CLOSED];
        int high = open ? delays[MAX_OPEN] : delays[MAX_CLOSED];

        //we indicate !randomness by a negative high value
        if (high < 0) {
//            System.out.println("HHControl "+ mechID + "is not random");
            //low is default
            return low;
        }
        //fast! calculation of time
        return (long) ((high - low) * r.nextDouble() + low);
    }

    public void setMaster(boolean b) {
        master = b;
    }

    public void setEnabled(boolean b) {
        enabled = b;
        if (!auto && master) {
            if (enabled) {
                hhm.extend();
            } else {
                hhm.retract();
            }
        }
    }

    public void setAuto(boolean b) {
        if (!auto && master) {
            if (enabled) {
                hhm.extend();
            } else {
                hhm.retract();
            }
        }
    }

    public boolean getEnabled() {
        return enabled;
    }

    public boolean getMaster() {
        return master;
    }

    public boolean getAuto() {
        return auto;
    }
}
